#include <bits/stdc++.h>
// 2024/07/27
using namespace std;

vector<vector<double>> TrainingSet;
vector<double> W, TestSet;

const double ALPHA = 0.005;
const double H = 1e-6;
const int SIZE = 2;
const double GRADIENT_THRESHOLD = 1e-4;

double PD(double (*func)(void), int i)
{
    W[i] += H;
    double t = func();
    W[i] -= H * 2;
    double v = func();
    W[i] += H;
    return (t - v) / (2 * H);
}

double Predict(int x, bool t = true)
{
    double result = 0;
    for (int i = 0; i < SIZE; ++i)
        result += (t ? W[i] * TrainingSet[x][i] : W[i] * TestSet[i]);
    return result + W[SIZE];
}

double L(void)
{
    double sum = 0;
    for (int i = 0; i < TrainingSet.size(); ++i)
        sum += pow(TrainingSet[i][SIZE] - Predict(i), 2); // 修正索引
    return sum / TrainingSet.size();
}

void Training(void)
{
    W.resize(SIZE + 1, 0.1);
    double l = 0;
    int cnt = 0;
    while (true) {
        for (int i = 0; i < 100; i++) {
            l = L();
            cnt++;

            vector<double> gradients(SIZE + 1);
            double gradient_norm = 0.0;

            for (int i = 0; i <= SIZE; i++) {
                gradients[i] = PD(L, i);
                gradient_norm += gradients[i] * gradients[i];
            }

            gradient_norm = sqrt(gradient_norm);

            if (gradient_norm < GRADIENT_THRESHOLD) {
                cout << "Gradient norm " << gradient_norm << " below threshold. Stopping training." << endl;
                return;
            }

            for (int i = 0; i <= SIZE; i++)
                W[i] -= ALPHA * gradients[i];
        }
        cout << "Iteration #" << cnt << ": Loss = " << l << endl;
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    ifstream input("data.txt");
    int TrainingSize;
    input >> TrainingSize;
    TrainingSet.resize(TrainingSize);
    for (int i = 0; i < TrainingSize; ++i) {
        TrainingSet[i].resize(SIZE + 1);
        for (int j = 0; j <= SIZE; ++j)
            input >> TrainingSet[i][j];
        TrainingSet[i][0] *= TrainingSet[i][0];
    }
    Training();
    ofstream of("W.txt");
    for (double w : W)
        of << w << " ";
    of << endl;
    return 0;
}
